/*
 *  Copyright (C) 2010-2013 JPEXS
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.jpexs.decompiler.flash.gui;

import com.jpexs.decompiler.flash.Configuration;
import com.jpexs.decompiler.flash.Main;
import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.SWFOutputStream;
import com.jpexs.decompiler.flash.abc.gui.ABCPanel;
import com.jpexs.decompiler.flash.abc.gui.ClassesListTreeModel;
import com.jpexs.decompiler.flash.abc.gui.DeobfuscationDialog;
import com.jpexs.decompiler.flash.abc.gui.TreeElement;
import com.jpexs.decompiler.flash.abc.gui.TreeLeafScript;
import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitClass;
import com.jpexs.decompiler.flash.action.gui.ActionPanel;
import com.jpexs.decompiler.flash.gui.player.FlashPlayerPanel;
import com.jpexs.decompiler.flash.helpers.Helper;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsLossless2Tag;
import com.jpexs.decompiler.flash.tags.DefineBitsLosslessTag;
import com.jpexs.decompiler.flash.tags.DefineBitsTag;
import com.jpexs.decompiler.flash.tags.DefineButton2Tag;
import com.jpexs.decompiler.flash.tags.DefineButtonTag;
import com.jpexs.decompiler.flash.tags.DefineEditTextTag;
import com.jpexs.decompiler.flash.tags.DefineFont2Tag;
import com.jpexs.decompiler.flash.tags.DefineFont3Tag;
import com.jpexs.decompiler.flash.tags.DefineFont4Tag;
import com.jpexs.decompiler.flash.tags.DefineFontTag;
import com.jpexs.decompiler.flash.tags.DefineMorphShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineMorphShapeTag;
import com.jpexs.decompiler.flash.tags.DefineShape2Tag;
import com.jpexs.decompiler.flash.tags.DefineShape3Tag;
import com.jpexs.decompiler.flash.tags.DefineShape4Tag;
import com.jpexs.decompiler.flash.tags.DefineShapeTag;
import com.jpexs.decompiler.flash.tags.DefineSoundTag;
import com.jpexs.decompiler.flash.tags.DefineSpriteTag;
import com.jpexs.decompiler.flash.tags.DefineText2Tag;
import com.jpexs.decompiler.flash.tags.DefineTextTag;
import com.jpexs.decompiler.flash.tags.DefineVideoStreamTag;
import com.jpexs.decompiler.flash.tags.DoABCTag;
import com.jpexs.decompiler.flash.tags.DoInitActionTag;
import com.jpexs.decompiler.flash.tags.EndTag;
import com.jpexs.decompiler.flash.tags.ExportAssetsTag;
import com.jpexs.decompiler.flash.tags.JPEGTablesTag;
import com.jpexs.decompiler.flash.tags.PlaceObject2Tag;
import com.jpexs.decompiler.flash.tags.PlaceObjectTypeTag;
import com.jpexs.decompiler.flash.tags.SetBackgroundColorTag;
import com.jpexs.decompiler.flash.tags.ShowFrameTag;
import com.jpexs.decompiler.flash.tags.SoundStreamBlockTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHead2Tag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTag;
import com.jpexs.decompiler.flash.tags.SoundStreamHeadTypeTag;
import com.jpexs.decompiler.flash.tags.Tag;
import com.jpexs.decompiler.flash.tags.base.ASMSource;
import com.jpexs.decompiler.flash.tags.base.AloneTag;
import com.jpexs.decompiler.flash.tags.base.BoundedTag;
import com.jpexs.decompiler.flash.tags.base.CharacterTag;
import com.jpexs.decompiler.flash.tags.base.Container;
import com.jpexs.decompiler.flash.tags.base.FontTag;
import com.jpexs.decompiler.flash.types.GLYPHENTRY;
import com.jpexs.decompiler.flash.types.MATRIX;
import com.jpexs.decompiler.flash.types.RECT;
import com.jpexs.decompiler.flash.types.RGB;
import com.jpexs.decompiler.flash.types.TEXTRECORD;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingWorker;
import javax.swing.border.BevelBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

/**
 *
 * @author Jindra
 */
public class MainFrame extends JFrame implements ActionListener, TreeSelectionListener {

   private SWF swf;
   public ABCPanel abcPanel;
   public ActionPanel actionPanel;
   private JTabbedPane tabPane;
   public LoadingPanel loadingPanel = new LoadingPanel(20, 20);
   public JLabel statusLabel = new JLabel("");
   public JPanel statusPanel = new JPanel();
   public JProgressBar progressBar = new JProgressBar(0, 100);
   private DeobfuscationDialog deobfuscationDialog;
   public JTree tagTree;
   public FlashPlayerPanel flashPanel;
   public JPanel displayPanel;
   public ImagePanel imagePanel;
   final static String CARDFLASHPANEL = "Flash card";
   final static String CARDIMAGEPANEL = "Image card";
   final static String CARDEMPTYPANEL = "Empty card";
   final static String CARDACTIONSCRIPTPANEL = "ActionScript card";
   final static String DETAILCARDAS3NAVIGATOR = "Traits list";
   final static String DETAILCARDEMPTYPANEL = "Empty card";
   private JPEGTablesTag jtt;
   private HashMap<Integer, CharacterTag> characters;
   private List<DoABCTag> abcList;
   JSplitPane splitPane1;
   JSplitPane splitPane2;
   private JPanel detailPanel;
   private JTextField filterField = new JTextField("");
   private JPanel searchPanel;

   public void setPercent(int percent) {
      progressBar.setValue(percent);
      progressBar.setVisible(true);
   }

   public void hidePercent() {
      if (progressBar.isVisible()) {
         progressBar.setVisible(false);
      }
   }

   static {
      try {
         File.createTempFile("temp", ".swf").delete(); //First call to this is slow, so make it first
      } catch (IOException ex) {
         Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
      }
   }

   private static void addTab(JTabbedPane tabbedPane, Component tab, String title, Icon icon) {
      tabbedPane.add(tab);

      JLabel lbl = new JLabel(title);
      lbl.setIcon(icon);
      lbl.setIconTextGap(5);
      lbl.setHorizontalTextPosition(SwingConstants.RIGHT);

      tabbedPane.setTabComponentAt(tabbedPane.getTabCount() - 1, lbl);
   }

   public void setStatus(String s) {
      statusLabel.setText(s);
   }

   public void setWorkStatus(String s) {
      if (s.equals("")) {
         loadingPanel.setVisible(false);
      } else {
         loadingPanel.setVisible(true);
      }
      statusLabel.setText(s);
   }

   public MainFrame(SWF swf) {
      setSize(1000, 700);
      tabPane = new JTabbedPane();
      View.setWindowIcon(this);
      addWindowListener(new WindowAdapter() {
         @Override
         public void windowClosing(WindowEvent e) {
            if (Main.proxyFrame != null) {
               if (Main.proxyFrame.isVisible()) {
                  return;
               }
            }
            Main.exit();
         }
      });
      setTitle(Main.applicationName + (Main.DISPLAY_FILENAME ? " - " + Main.getFileTitle() : ""));
      JMenuBar menuBar = new JMenuBar();

      JMenu menuFile = new JMenu("File");
      JMenuItem miOpen = new JMenuItem("Open...");
      miOpen.setIcon(View.getIcon("open16"));
      miOpen.setActionCommand("OPEN");
      miOpen.addActionListener(this);
      JMenuItem miSave = new JMenuItem("Save");
      miSave.setIcon(View.getIcon("save16"));
      miSave.setActionCommand("SAVE");
      miSave.addActionListener(this);
      JMenuItem miSaveAs = new JMenuItem("Save as...");
      miSaveAs.setIcon(View.getIcon("saveas16"));
      miSaveAs.setActionCommand("SAVEAS");
      miSaveAs.addActionListener(this);
      JMenuItem menuExportAll = new JMenuItem("Export all");
      menuExportAll.setActionCommand("EXPORT");
      menuExportAll.addActionListener(this);
      JMenuItem menuExportSel = new JMenuItem("Export selection");
      menuExportSel.setActionCommand("EXPORTSEL");
      menuExportSel.addActionListener(this);
      menuExportAll.setIcon(View.getIcon("export16"));
      menuExportSel.setIcon(View.getIcon("exportsel16"));



      menuFile.add(miOpen);
      menuFile.add(miSave);
      menuFile.add(miSaveAs);
      menuFile.add(menuExportAll);
      menuFile.add(menuExportSel);
      menuFile.addSeparator();
      JMenuItem miClose = new JMenuItem("Exit");
      miClose.setIcon(View.getIcon("exit16"));
      miClose.setActionCommand("EXIT");
      miClose.addActionListener(this);
      menuFile.add(miClose);
      menuBar.add(menuFile);
      JMenu menuDeobfuscation = new JMenu("Deobfuscation");
      menuDeobfuscation.setIcon(View.getIcon("deobfuscate16"));

      JMenuItem miDeobfuscation = new JMenuItem("PCode deobfuscation...");
      miDeobfuscation.setActionCommand("DEOBFUSCATE");
      miDeobfuscation.addActionListener(this);

      JCheckBoxMenuItem miSubLimiter = new JCheckBoxMenuItem("Enable sub limiter");
      miSubLimiter.setActionCommand("SUBLIMITER");
      miSubLimiter.addActionListener(this);

      JMenuItem miRenameIdentifiers = new JMenuItem("Rename identifiers");
      miRenameIdentifiers.setActionCommand("RENAMEIDENTIFIERS");
      miRenameIdentifiers.addActionListener(this);

      JMenuItem miRemoveDeadCode = new JMenuItem("Remove dead code");
      miRemoveDeadCode.setActionCommand("REMOVEDEADCODE");
      miRemoveDeadCode.addActionListener(this);

      JMenuItem miRemoveDeadCodeAll = new JMenuItem("Remove all dead code");
      miRemoveDeadCodeAll.setActionCommand("REMOVEDEADCODEALL");
      miRemoveDeadCodeAll.addActionListener(this);

      JMenuItem miTraps = new JMenuItem("Remove traps");
      miTraps.setActionCommand("REMOVETRAPS");
      miTraps.addActionListener(this);

      JMenuItem miTrapsAll = new JMenuItem("Remove all traps");
      miTrapsAll.setActionCommand("REMOVETRAPSALL");
      miTrapsAll.addActionListener(this);

      JMenuItem miControlFlow = new JMenuItem("Restore control flow");
      miControlFlow.setActionCommand("RESTORECONTROLFLOW");
      miControlFlow.addActionListener(this);

      JMenuItem miControlFlowAll = new JMenuItem("Restore all control flow");
      miControlFlowAll.setActionCommand("RESTORECONTROLFLOWALL");
      miControlFlowAll.addActionListener(this);


      menuDeobfuscation.add(miRenameIdentifiers);
      //menuDeobfuscation.add(miSubLimiter);
      menuDeobfuscation.add(miDeobfuscation);
      /*menuDeobfuscation.add(miDeobfuscate);
       menuDeobfuscation.addSeparator();*/
      /*menuDeobfuscation.add(miRemoveDeadCode);
       menuDeobfuscation.add(miRemoveDeadCodeAll);
       menuDeobfuscation.add(miTraps);
       menuDeobfuscation.add(miTrapsAll);
       menuDeobfuscation.add(miControlFlow);
       menuDeobfuscation.add(miControlFlowAll);
       */
      JMenu menuTools = new JMenu("Tools");
      JMenuItem miProxy = new JMenuItem("Proxy");
      miProxy.setActionCommand("SHOWPROXY");
      miProxy.setIcon(View.getIcon("proxy16"));
      miProxy.addActionListener(this);
      menuTools.add(miProxy);

      //menuTools.add(menuDeobfuscation);
      menuTools.add(menuDeobfuscation);
      menuBar.add(menuTools);

      JMenu menuHelp = new JMenu("Help");
      JMenuItem miAbout = new JMenuItem("About...");
      miAbout.setIcon(View.getIcon("about16"));

      miAbout.setActionCommand("ABOUT");
      miAbout.addActionListener(this);

      JMenuItem miCheckUpdates = new JMenuItem("Check for updates...");
      miCheckUpdates.setActionCommand("CHECKUPDATES");
      miCheckUpdates.setIcon(View.getIcon("update16"));
      miCheckUpdates.addActionListener(this);

      JMenuItem miDonate = new JMenuItem("Donate...");
      miDonate.setActionCommand("DONATE");
      miDonate.setIcon(View.getIcon("donate16"));
      miDonate.addActionListener(this);

      JMenuItem miHomepage = new JMenuItem("Visit homepage");
      miHomepage.setActionCommand("HOMEPAGE");
      miHomepage.setIcon(View.getIcon("homepage16"));
      miHomepage.addActionListener(this);


      menuHelp.add(miCheckUpdates);
      menuHelp.add(miDonate);
      menuHelp.add(miHomepage);
      menuHelp.add(miAbout);
      menuBar.add(menuHelp);

      setJMenuBar(menuBar);
      List<Object> objs = new ArrayList<Object>();
      objs.addAll(swf.tags);


      this.swf = swf;
      java.awt.Container cnt = getContentPane();
      cnt.setLayout(new BorderLayout());


      detailPanel = new JPanel();
      detailPanel.setLayout(new CardLayout());
      JPanel whitePanel = new JPanel();
      whitePanel.setBackground(Color.white);
      detailPanel.add(whitePanel, DETAILCARDEMPTYPANEL);
      CardLayout cl2 = (CardLayout) (detailPanel.getLayout());
      cl2.show(detailPanel, DETAILCARDEMPTYPANEL);


      abcList = new ArrayList<DoABCTag>();
      getActionScript3(objs, abcList);
      if (!abcList.isEmpty()) {
         addTab(tabPane, abcPanel = new ABCPanel(abcList), "ActionScript3", View.getIcon("as16"));
         detailPanel.add(abcPanel.tabbedPane, DETAILCARDAS3NAVIGATOR);
      } else {
         actionPanel = new ActionPanel();
         addTab(tabPane, actionPanel, "ActionScript", View.getIcon("as16"));

         menuDeobfuscation.setEnabled(false);
      }


      tagTree = new JTree(new TagTreeModel(createTagList(objs, null), (new File(Main.file)).getName()));
      tagTree.addTreeSelectionListener(this);
      TreeCellRenderer tcr = new DefaultTreeCellRenderer() {
         @Override
         public Component getTreeCellRendererComponent(
                 JTree tree,
                 Object value,
                 boolean sel,
                 boolean expanded,
                 boolean leaf,
                 int row,
                 boolean hasFocus) {

            super.getTreeCellRendererComponent(
                    tree, value, sel,
                    expanded, leaf, row,
                    hasFocus);
            Object val = value;
            if (val instanceof TagNode) {
               val = ((TagNode) val).tag;
            }
            String type = getTagType(val);
            if (row == 0) {
               setIcon(View.getIcon("flash16"));
            } else if (type != null) {
               if (type.equals("folder") && expanded) {
                  type = "folderopen";
               }
               setIcon(View.getIcon(type + "16"));
               //setToolTipText("This book is in the Tutorial series.");
            } else {
               //setToolTipText(null); //no tool tip
            }
            return this;
         }
      };

      tagTree.setCellRenderer(tcr);
      loadingPanel.setPreferredSize(new Dimension(30, 30));
      statusPanel = new JPanel();
      statusPanel.setPreferredSize(new Dimension(1, 30));
      statusPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
      statusPanel.setLayout(new BorderLayout());
      statusPanel.add(loadingPanel, BorderLayout.WEST);
      statusPanel.add(statusLabel, BorderLayout.CENTER);
      loadingPanel.setVisible(false);
      cnt.add(statusPanel, BorderLayout.SOUTH);

      for (Tag t : swf.tags) {
         if (t instanceof JPEGTablesTag) {
            jtt = (JPEGTablesTag) t;
         }
      }
      characters = new HashMap<Integer, CharacterTag>();
      List<Object> list2 = new ArrayList<Object>();
      list2.addAll(swf.tags);
      parseCharacters(list2);

      //setLayout(new BorderLayout());
      try {
         flashPanel = new FlashPlayerPanel(this);
      } catch (FlashUnsupportedException fue) {
      }
      displayPanel = new JPanel(new CardLayout());
      if (flashPanel != null) {
         displayPanel.add(flashPanel, CARDFLASHPANEL);
      } else {
         JPanel swtPanel = new JPanel(new BorderLayout());
         swtPanel.add(new JLabel("<html><center>Preview of this object is not available on this platform. (Windows only)</center></html>", JLabel.CENTER), BorderLayout.CENTER);
         swtPanel.setBackground(Color.white);
         displayPanel.add(swtPanel, CARDFLASHPANEL);
      }
      imagePanel = new ImagePanel();
      displayPanel.add(imagePanel, CARDIMAGEPANEL);
      displayPanel.add(new JPanel(), CARDEMPTYPANEL);
      if (actionPanel != null) {
         displayPanel.add(actionPanel, CARDACTIONSCRIPTPANEL);
      }
      if (abcPanel != null) {
         displayPanel.add(abcPanel, CARDACTIONSCRIPTPANEL);
      }
      CardLayout cl = (CardLayout) (displayPanel.getLayout());
      cl.show(displayPanel, CARDEMPTYPANEL);

      searchPanel = new JPanel();
      searchPanel.setLayout(new BorderLayout());
      searchPanel.add(filterField, BorderLayout.CENTER);
      searchPanel.add(new JLabel(View.getIcon("search16")), BorderLayout.WEST);
      JLabel closeSearchButton = new JLabel(View.getIcon("cancel16"));
      closeSearchButton.addMouseListener(new MouseAdapter() {
         @Override
         public void mouseClicked(MouseEvent e) {
            filterField.setText("");
            doFilter();
            searchPanel.setVisible(false);
         }
      });
      searchPanel.add(closeSearchButton, BorderLayout.EAST);
      JPanel pan1 = new JPanel(new BorderLayout());
      pan1.add(new JScrollPane(tagTree), BorderLayout.CENTER);
      pan1.add(searchPanel, BorderLayout.SOUTH);

      filterField.setActionCommand("FILTERSCRIPT");
      filterField.addActionListener(this);

      searchPanel.setVisible(false);

      filterField.getDocument().addDocumentListener(new DocumentListener() {
         @Override
         public void changedUpdate(DocumentEvent e) {
            warn();
         }

         @Override
         public void removeUpdate(DocumentEvent e) {
            warn();
         }

         @Override
         public void insertUpdate(DocumentEvent e) {
            warn();
         }

         public void warn() {
            doFilter();
         }
      });

      //displayPanel.setBorder(BorderFactory.createLineBorder(Color.black));
      splitPane2 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, pan1, detailPanel);
      splitPane1 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPane2, displayPanel);
      cnt.add(splitPane1, BorderLayout.CENTER);
      splitPane1.setDividerLocation(0.5);
      View.centerScreen(this);
      tagTree.addKeyListener(new KeyAdapter() {
         @Override
         public void keyPressed(KeyEvent e) {
            if ((e.getKeyCode() == 'F') && (e.isControlDown())) {
               searchPanel.setVisible(true);
               filterField.requestFocusInWindow();
            }
         }
      });
      detailPanel.setVisible(false);

   }

   public void doFilter() {
      TagNode n = getASTagNode(tagTree);
      if (n != null) {
         if (n.tag instanceof ClassesListTreeModel) {
            n.tag = new ClassesListTreeModel(abcPanel.classTree.treeList, filterField.getText());
         }
         tagTree.updateUI();
      }
   }

   @Override
   public void setVisible(boolean b) {
      super.setVisible(b);
      if (b) {
         if (abcPanel != null) {
            abcPanel.initSplits();
         }
         if (actionPanel != null) {
            actionPanel.initSplits();
         }
         splitPane1.setDividerLocation(getWidth() / 3);
         splitPane2.setDividerLocation(splitPane2.getHeight() * 3 / 5);
         splitPos = splitPane2.getDividerLocation();
      }
   }

   private void parseCharacters(List<Object> list) {
      for (Object t : list) {
         if (t instanceof CharacterTag) {
            characters.put(((CharacterTag) t).getCharacterID(), (CharacterTag) t);
         }
         if (t instanceof Container) {
            parseCharacters(((Container) t).getSubItems());
         }
      }
   }

   public static void getShapes(List<Object> list, List<Tag> shapes) {
      for (Object t : list) {
         if (t instanceof Container) {
            getShapes(((Container) t).getSubItems(), shapes);
         }
         if ((t instanceof DefineShapeTag)
                 || (t instanceof DefineShape2Tag)
                 || (t instanceof DefineShape3Tag)
                 || (t instanceof DefineShape4Tag)) {
            shapes.add((Tag) t);
         }
      }
   }

   public static void getFonts(List<Object> list, List<Tag> fonts) {
      for (Object t : list) {
         if (t instanceof Container) {
            getFonts(((Container) t).getSubItems(), fonts);
         }
         if ((t instanceof DefineFontTag)
                 || (t instanceof DefineFont2Tag)
                 || (t instanceof DefineFont3Tag)
                 || (t instanceof DefineFont4Tag)) {
            fonts.add((Tag) t);
         }
      }
   }

   public static void getActionScript3(List<Object> list, List<DoABCTag> actionScripts) {
      for (Object t : list) {
         if (t instanceof Container) {
            getActionScript3(((Container) t).getSubItems(), actionScripts);
         }
         if (t instanceof DoABCTag) {
            actionScripts.add((DoABCTag) t);
         }
      }
   }

   public static void getMorphShapes(List<Object> list, List<Tag> morphShapes) {
      for (Object t : list) {
         if (t instanceof Container) {
            getMorphShapes(((Container) t).getSubItems(), morphShapes);
         }
         if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) {
            morphShapes.add((Tag) t);
         }
      }
   }

   public static void getImages(List<Object> list, List<Tag> images) {
      for (Object t : list) {
         if (t instanceof Container) {
            getImages(((Container) t).getSubItems(), images);
         }
         if ((t instanceof DefineBitsTag)
                 || (t instanceof DefineBitsJPEG2Tag)
                 || (t instanceof DefineBitsJPEG3Tag)
                 || (t instanceof DefineBitsJPEG4Tag)
                 || (t instanceof DefineBitsLosslessTag)
                 || (t instanceof DefineBitsLossless2Tag)) {
            images.add((Tag) t);
         }
      }
   }

   public static void getTexts(List<Object> list, List<Tag> texts) {
      for (Object t : list) {
         if (t instanceof Container) {
            getTexts(((Container) t).getSubItems(), texts);
         }
         if ((t instanceof DefineTextTag)
                 || (t instanceof DefineText2Tag)
                 || (t instanceof DefineEditTextTag)) {
            texts.add((Tag) t);
         }
      }
   }

   public static void getSprites(List<Object> list, List<Tag> sprites) {
      for (Object t : list) {
         if (t instanceof Container) {
            getSprites(((Container) t).getSubItems(), sprites);
         }
         if (t instanceof DefineSpriteTag) {
            sprites.add((Tag) t);
         }
      }
   }

   public static void getButtons(List<Object> list, List<Tag> buttons) {
      for (Object t : list) {
         if (t instanceof Container) {
            getButtons(((Container) t).getSubItems(), buttons);
         }
         if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) {
            buttons.add((Tag) t);
         }
      }
   }

   public static List<TagNode> createASTagList(List<Object> list, Object parent) {
      List<TagNode> ret = new ArrayList<TagNode>();
      int frame = 1;
      List<TagNode> frames = new ArrayList<TagNode>();

      List<ExportAssetsTag> exportAssetsTags = new ArrayList<ExportAssetsTag>();
      for (Object t : list) {
         if (t instanceof ExportAssetsTag) {
            exportAssetsTags.add((ExportAssetsTag) t);
         }
         if (t instanceof ShowFrameTag) {
            TagNode tti = new TagNode(new FrameNode(frame, parent, false));

            for (int r = ret.size() - 1; r >= 0; r--) {
               if (!(ret.get(r).tag instanceof DefineSpriteTag)) {
                  if (!(ret.get(r).tag instanceof DefineButtonTag)) {
                     if (!(ret.get(r).tag instanceof DefineButton2Tag)) {
                        if (!(ret.get(r).tag instanceof DoInitActionTag)) {
                           tti.subItems.add(ret.get(r));
                           ret.remove(r);
                        }
                     }
                  }
               }
            }
            frame++;
            frames.add(tti);
         } else if (t instanceof ASMSource) {
            TagNode tti = new TagNode(t);
            ret.add(tti);
         } else if (t instanceof Container) {
            if (((Container) t).getItemCount() > 0) {

               TagNode tti = new TagNode(t);
               List<Object> subItems = ((Container) t).getSubItems();

               tti.subItems = createASTagList(subItems, t);
               ret.add(tti);
            }
         }

      }
      ret.addAll(frames);
      for (int i = ret.size() - 1; i >= 0; i--) {
         if (ret.get(i).tag instanceof DefineSpriteTag) {
            ((DefineSpriteTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags;
         }
         if (ret.get(i).tag instanceof DefineButtonTag) {
            ((DefineButtonTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags;
         }
         if (ret.get(i).tag instanceof DefineButton2Tag) {
            ((DefineButton2Tag) ret.get(i).tag).exportAssetsTags = exportAssetsTags;
         }
         if (ret.get(i).tag instanceof DoInitActionTag) {
            ((DoInitActionTag) ret.get(i).tag).exportAssetsTags = exportAssetsTags;
         }
         if (ret.get(i).tag instanceof ASMSource) {
            ASMSource ass = (ASMSource) ret.get(i).tag;
            if (ass.containsSource()) {
               continue;
            }
         }
         if (ret.get(i).subItems.isEmpty()) {
            ret.remove(i);
         }
      }
      return ret;
   }

   public List<TagNode> getSelectedNodes() {
      List<TagNode> ret = new ArrayList<TagNode>();
      TreePath tps[] = tagTree.getSelectionPaths();
      if (tps == null) {
         return ret;
      }
      for (TreePath tp : tps) {
         TagNode te = (TagNode) tp.getLastPathComponent();
         ret.add(te);
      }
      return ret;
   }

   public String getTagType(Object t) {
      if ((t instanceof DefineFontTag)
              || (t instanceof DefineFont2Tag)
              || (t instanceof DefineFont3Tag)
              || (t instanceof DefineFont4Tag)) {
         return "font";
      }
      if ((t instanceof DefineTextTag)
              || (t instanceof DefineText2Tag)
              || (t instanceof DefineEditTextTag)) {
         return "text";
      }

      if ((t instanceof DefineBitsTag)
              || (t instanceof DefineBitsJPEG2Tag)
              || (t instanceof DefineBitsJPEG3Tag)
              || (t instanceof DefineBitsJPEG4Tag)
              || (t instanceof DefineBitsLosslessTag)
              || (t instanceof DefineBitsLossless2Tag)) {
         return "image";
      }
      if ((t instanceof DefineShapeTag)
              || (t instanceof DefineShape2Tag)
              || (t instanceof DefineShape3Tag)
              || (t instanceof DefineShape4Tag)) {
         return "shape";
      }

      if ((t instanceof DefineMorphShapeTag) || (t instanceof DefineMorphShape2Tag)) {
         return "morphshape";
      }

      if (t instanceof DefineSpriteTag) {
         return "sprite";
      }
      if ((t instanceof DefineButtonTag) || (t instanceof DefineButton2Tag)) {
         return "button";
      }
      if (t instanceof ASMSource) {
         return "as";
      }
      if (t instanceof TreeElement) {
         TreeElement te = (TreeElement) t;
         if (te.getItem() instanceof TreeLeafScript) {
            return "as";
         } else {
            return "package";
         }
      }
      if (t instanceof FrameNode) {
         return "frame";
      }
      if (t instanceof ShowFrameTag) {
         return "showframe";
      }

      if (t instanceof DefineVideoStreamTag) {
         return "movie";
      }

      if ((t instanceof DefineSoundTag) || (t instanceof SoundStreamHeadTag) || (t instanceof SoundStreamHead2Tag)) {
         return "sound";
      }
      return "folder";
   }

   public List<Object> getTagsWithType(List<Object> list, String type) {
      List<Object> ret = new ArrayList<Object>();
      for (Object o : list) {
         String ttype = getTagType(o);
         if (type.equals(ttype)) {
            ret.add(o);
         }
      }
      return ret;
   }

   public List<TagNode> getTagNodesWithType(List<Object> list, String type, Object parent, boolean display) {
      List<TagNode> ret = new ArrayList<TagNode>();
      int frameCnt = 0;
      for (Object o : list) {
         String ttype = getTagType(o);
         if ("showframe".equals(ttype) && "frame".equals(type)) {
            frameCnt++;
            ret.add(new TagNode(new FrameNode(frameCnt, parent, display)));
         } else if (type.equals(ttype)) {
            ret.add(new TagNode(o));
         }
      }
      return ret;
   }

   public List<Object> getAllSubs(JTree tree, Object o) {
      TreeModel tm = tree.getModel();
      List<Object> ret = new ArrayList<Object>();
      for (int i = 0; i < tm.getChildCount(o); i++) {
         Object c = tm.getChild(o, i);
         ret.add(c);
         ret.addAll(getAllSubs(tree, c));
      }
      return ret;
   }

   public List<Object> getAllSelected(JTree tree) {
      TreeSelectionModel tsm = tree.getSelectionModel();
      TreePath tps[] = tsm.getSelectionPaths();
      List<Object> ret = new ArrayList<Object>();
      if (tps == null) {
         return ret;
      }

      for (TreePath tp : tps) {
         Object o = tp.getLastPathComponent();
         ret.add(o);
         ret.addAll(getAllSubs(tree, o));
      }
      return ret;
   }

   public TagNode getASTagNode(JTree tree) {
      TreeModel tm = tree.getModel();
      Object root = tm.getRoot();
      for (int i = 0; i < tm.getChildCount(root); i++) {
         Object node = tm.getChild(root, i);
         if (node instanceof TagNode) {
            Object tag = ((TagNode) tm.getChild(root, i)).tag;
            if (tag != null) {
               if (tag.toString().equals("scripts")) {
                  return (TagNode) node;
               }
            }
         }
      }
      return null;
   }

   public List<TagNode> createTagList(List<Object> list, Object parent) {
      List<TagNode> ret = new ArrayList<TagNode>();
      List<TagNode> frames = getTagNodesWithType(list, "frame", parent, true);
      List<TagNode> shapes = getTagNodesWithType(list, "shape", parent, true);
      List<TagNode> morphShapes = getTagNodesWithType(list, "morphshape", parent, true);
      List<TagNode> sprites = getTagNodesWithType(list, "sprite", parent, true);
      List<TagNode> buttons = getTagNodesWithType(list, "button", parent, true);
      List<TagNode> images = getTagNodesWithType(list, "image", parent, true);
      List<TagNode> fonts = getTagNodesWithType(list, "font", parent, true);
      List<TagNode> texts = getTagNodesWithType(list, "text", parent, true);
      List<TagNode> movies = getTagNodesWithType(list, "movie", parent, true);
      List<TagNode> sounds = getTagNodesWithType(list, "sound", parent, true);
      List<TagNode> actionScript = new ArrayList<TagNode>();

      for (int i = 0; i < sounds.size(); i++) {
         if (sounds.get(i).tag instanceof SoundStreamHeadTypeTag) {
            List<SoundStreamBlockTag> blocks = new ArrayList<SoundStreamBlockTag>();
            SWF.populateSoundStreamBlocks(list, (Tag) sounds.get(i).tag, blocks);
            if (blocks.isEmpty()) {
               sounds.remove(i);
               i--;
            }
         }
      }

      for (TagNode n : sprites) {
         n.subItems = getTagNodesWithType(new ArrayList<Object>(((DefineSpriteTag) n.tag).subTags), "frame", n.tag, true);
      }

      List<ExportAssetsTag> exportAssetsTags = new ArrayList<ExportAssetsTag>();
      for (Object t : list) {
         if (t instanceof ExportAssetsTag) {
            exportAssetsTags.add((ExportAssetsTag) t);
         }
         /*if (t instanceof ASMSource) {
          TagNode tti = new TagNode(t);
          ret.add(tti);
          } else */
         if (t instanceof Container) {
            TagNode tti = new TagNode(t);
            if (((Container) t).getItemCount() > 0) {
               List<Object> subItems = ((Container) t).getSubItems();
               tti.subItems = createTagList(subItems, parent);
            }
            //ret.add(tti);
         }
      }

      actionScript = createASTagList(list, null);
      TagNode textsNode = new TagNode("texts");
      textsNode.subItems.addAll(texts);

      TagNode imagesNode = new TagNode("images");
      imagesNode.subItems.addAll(images);

      TagNode moviesNode = new TagNode("movies");
      moviesNode.subItems.addAll(movies);

      TagNode soundsNode = new TagNode("sounds");
      soundsNode.subItems.addAll(sounds);


      TagNode fontsNode = new TagNode("fonts");
      fontsNode.subItems.addAll(fonts);


      TagNode spritesNode = new TagNode("sprites");
      spritesNode.subItems.addAll(sprites);

      TagNode shapesNode = new TagNode("shapes");
      shapesNode.subItems.addAll(shapes);

      TagNode morphShapesNode = new TagNode("morphshapes");
      morphShapesNode.subItems.addAll(morphShapes);

      TagNode buttonsNode = new TagNode("buttons");
      buttonsNode.subItems.addAll(buttons);

      TagNode framesNode = new TagNode("frames");
      framesNode.subItems.addAll(frames);

      TagNode actionScriptNode = new TagNode("scripts");
      actionScriptNode.subItems.addAll(actionScript);

      if (!shapesNode.subItems.isEmpty()) {
         ret.add(shapesNode);
      }
      if (!morphShapesNode.subItems.isEmpty()) {
         ret.add(morphShapesNode);
      }
      if (!spritesNode.subItems.isEmpty()) {
         ret.add(spritesNode);
      }
      if (!textsNode.subItems.isEmpty()) {
         ret.add(textsNode);
      }
      if (!imagesNode.subItems.isEmpty()) {
         ret.add(imagesNode);
      }
      if (!moviesNode.subItems.isEmpty()) {
         ret.add(moviesNode);
      }
      if (!soundsNode.subItems.isEmpty()) {
         ret.add(soundsNode);
      }
      if (!buttonsNode.subItems.isEmpty()) {
         ret.add(buttonsNode);
      }
      if (!fontsNode.subItems.isEmpty()) {
         ret.add(fontsNode);
      }
      if (!framesNode.subItems.isEmpty()) {
         ret.add(framesNode);
      }




      if (abcPanel != null) {
         actionScriptNode.subItems.clear();
         actionScriptNode.tag = abcPanel.classTree.getModel();
      }
      if ((!actionScriptNode.subItems.isEmpty()) || (abcPanel != null)) {
         ret.add(actionScriptNode);
      }

      return ret;
   }

   public boolean confirmExperimental() {
      return JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION;
   }

   public void actionPerformed(ActionEvent e) {
      if (e.getActionCommand().equals("EXIT")) {
         setVisible(false);
         if (Main.proxyFrame != null) {
            if (Main.proxyFrame.isVisible()) {
               return;
            }
         }
         Main.exit();
      }
      if (Main.isWorking()) {
         return;
      }

      if (e.getActionCommand().equals("ABOUT")) {
         Main.about();
      }


      if (e.getActionCommand().equals("SHOWPROXY")) {
         Main.showProxy();
      }

      if (e.getActionCommand().equals("SUBLIMITER")) {
         if (e.getSource() instanceof JCheckBoxMenuItem) {
            Main.setSubLimiter(((JCheckBoxMenuItem) e.getSource()).getState());
         }

      }

      if (e.getActionCommand().equals("SAVE")) {
         try {
            Main.saveFile(Main.file);
         } catch (IOException ex) {
            Logger.getLogger(com.jpexs.decompiler.flash.abc.gui.ABCPanel.class.getName()).log(Level.SEVERE, null, ex);
            JOptionPane.showMessageDialog(null, "Cannot save file", "Error", JOptionPane.ERROR_MESSAGE);
         }
      }
      if (e.getActionCommand().equals("SAVEAS")) {
         if (Main.saveFileDialog()) {
            setTitle(Main.applicationName + (Main.DISPLAY_FILENAME ? " - " + Main.getFileTitle() : ""));
         }
      }
      if (e.getActionCommand().equals("OPEN")) {
         Main.openFileDialog();

      }

      if (e.getActionCommand().startsWith("EXPORT")) {
         final ExportDialog export = new ExportDialog();
         export.setVisible(true);
         if (!export.cancelled) {
            JFileChooser chooser = new JFileChooser();
            chooser.setCurrentDirectory(new java.io.File((String) Configuration.getConfig("lastExportDir", ".")));
            chooser.setDialogTitle("Select directory to export");
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            chooser.setAcceptAllFileFilterUsed(false);
            if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
               final long timeBefore = System.currentTimeMillis();
               Main.startWork("Exporting...");
               final String selFile = chooser.getSelectedFile().getAbsolutePath();
               Configuration.setConfig("lastExportDir", chooser.getSelectedFile().getParentFile().getAbsolutePath());
               final boolean isPcode = export.getOption(ExportDialog.OPTION_ACTIONSCRIPT) == 1;
               final boolean isMp3 = export.getOption(ExportDialog.OPTION_SOUNDS) == 0;
               final boolean onlySel = e.getActionCommand().endsWith("SEL");
               (new Thread() {
                  @Override
                  public void run() {
                     try {
                        if (onlySel) {
                           List<Object> sel = getAllSelected(tagTree);

                           List<TreeLeafScript> tlsList = new ArrayList<TreeLeafScript>();
                           JPEGTablesTag jtt = null;
                           for (Tag t : swf.tags) {
                              if (t instanceof JPEGTablesTag) {
                                 jtt = (JPEGTablesTag) t;
                                 break;
                              }
                           }
                           List<Tag> images = new ArrayList<Tag>();
                           List<Tag> shapes = new ArrayList<Tag>();
                           List<Tag> movies = new ArrayList<Tag>();
                           List<Tag> sounds = new ArrayList<Tag>();
                           List<TagNode> actionNodes = new ArrayList<TagNode>();
                           for (Object d : sel) {
                              if (d instanceof TagNode) {
                                 TagNode n = (TagNode) d;
                                 if ("image".equals(getTagType(n.tag))) {
                                    images.add((Tag) n.tag);
                                 }
                                 if ("shape".equals(getTagType(n.tag))) {
                                    shapes.add((Tag) n.tag);
                                 }
                                 if ("as".equals(getTagType(n.tag))) {
                                    actionNodes.add(n);
                                 }
                                 if ("movie".equals(getTagType(n.tag))) {
                                    movies.add((Tag) n.tag);
                                 }
                                 if ("sound".equals(getTagType(n.tag))) {
                                    sounds.add((Tag) n.tag);
                                 }
                              }
                              if (d instanceof TreeElement) {
                                 if (((TreeElement) d).isLeaf()) {
                                    tlsList.add((TreeLeafScript) ((TreeElement) d).getItem());
                                 }
                              }
                           }
                           SWF.exportImages(selFile + File.separator + "images", images, jtt);
                           SWF.exportShapes(selFile + File.separator + "shapes", shapes);
                           swf.exportMovies(selFile + File.separator + "movies", movies);
                           swf.exportSounds(selFile + File.separator + "sounds", sounds, isMp3);
                           if (abcPanel != null) {
                              for (int i = 0; i < tlsList.size(); i++) {
                                 TreeLeafScript tls = tlsList.get(i);
                                 Main.startWork("Exporting " + (i + 1) + "/" + tlsList.size() + " " + tls.abc.script_info[tls.scriptIndex].getPath(tls.abc) + " ...");
                                 tls.abc.script_info[tls.scriptIndex].export(tls.abc, abcPanel.list, selFile, isPcode);
                              }
                           } else {
                              List<TagNode> allNodes = new ArrayList<TagNode>();
                              TagNode asn = getASTagNode(tagTree);
                              if (asn != null) {
                                 allNodes.add(asn);
                                 TagNode.setExport(allNodes, false);
                                 TagNode.setExport(actionNodes, true);
                                 TagNode.exportNodeAS(allNodes, selFile, isPcode);
                              }
                           }
                        } else {
                           swf.exportImages(selFile + File.separator + "images");
                           swf.exportShapes(selFile + File.separator + "shapes");
                           swf.exportMovies(selFile + File.separator + "movies");
                           swf.exportSounds(selFile + File.separator + "sounds", isMp3);
                           swf.exportActionScript(selFile, isPcode);
                        }
                     } catch (Exception ignored) {
                        ignored.printStackTrace();
                        JOptionPane.showMessageDialog(null, "Cannot write to the file");
                     }
                     Main.stopWork();
                     long timeAfter = System.currentTimeMillis();
                     long timeMs = timeAfter - timeBefore;
                     long timeS = timeMs / 1000;
                     timeMs = timeMs % 1000;
                     long timeM = timeS / 60;
                     timeS = timeS % 60;
                     long timeH = timeM / 60;
                     timeM = timeM % 60;
                     String timeStr = "";
                     if (timeH > 0) {
                        timeStr += Helper.padZeros(timeH, 2) + ":";
                     }
                     timeStr += Helper.padZeros(timeM, 2) + ":";
                     timeStr += Helper.padZeros(timeS, 2) + "." + Helper.padZeros(timeMs, 3);
                     setStatus("Exported in " + timeStr);
                  }
               }).start();

            }
         }
      }

      if (e.getActionCommand().equals("CHECKUPDATES")) {
         if (!Main.checkForUpdates()) {
            JOptionPane.showMessageDialog(null, "No new version available.");
         }
      }

      if (e.getActionCommand().equals("DONATE")) {
         String donateURL = Main.projectPage + "/donate.html";
         if (java.awt.Desktop.isDesktopSupported()) {
            java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
            try {
               java.net.URI uri = new java.net.URI(donateURL);
               desktop.browse(uri);
            } catch (Exception ex) {
            }
         } else {
            JOptionPane.showMessageDialog(null, "Please visit\r\n" + donateURL + "\r\nfor details.");
         }
      }

      if (e.getActionCommand().equals("HOMEPAGE")) {
         String homePageURL = Main.projectPage;
         if (java.awt.Desktop.isDesktopSupported()) {
            java.awt.Desktop desktop = java.awt.Desktop.getDesktop();
            try {
               java.net.URI uri = new java.net.URI(homePageURL);
               desktop.browse(uri);
            } catch (Exception ex) {
            }
         } else {
            JOptionPane.showMessageDialog(null, "Visit homepage at: \r\n" + homePageURL);
         }
      }

      if (e.getActionCommand().startsWith("RESTORECONTROLFLOW")) {
         Main.startWork("Restoring control flow...");
         final boolean all = e.getActionCommand().endsWith("ALL");
         if ((!all) || confirmExperimental()) {
            new SwingWorker() {
               @Override
               protected Object doInBackground() throws Exception {
                  int cnt = 0;
                  if (all) {
                     for (DoABCTag tag : abcPanel.list) {
                        tag.abc.restoreControlFlow();
                     }
                  } else {
                     int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
                     if (bi != -1) {
                        abcPanel.abc.bodies[bi].restoreControlFlow(abcPanel.abc.constants);
                     }
                     abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
                  }
                  Main.stopWork();
                  JOptionPane.showMessageDialog(null, "Control flow restored");
                  abcPanel.reload();
                  return true;
               }
            }.execute();
         }
      }

      if (e.getActionCommand().startsWith("REMOVETRAPS")) {
         Main.startWork("Removing traps...");
         final boolean all = e.getActionCommand().endsWith("ALL");
         if ((!all) || confirmExperimental()) {
            new SwingWorker() {
               @Override
               protected Object doInBackground() throws Exception {
                  int cnt = 0;
                  if (all) {
                     for (DoABCTag tag : abcPanel.list) {
                        cnt += tag.abc.removeTraps();
                     }
                  } else {
                     int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
                     if (bi != -1) {
                        cnt += abcPanel.abc.bodies[bi].removeTraps(abcPanel.abc.constants);
                     }
                     abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
                  }
                  Main.stopWork();
                  JOptionPane.showMessageDialog(null, "Traps removed: " + cnt);
                  abcPanel.reload();
                  return true;
               }
            }.execute();
         }
      }

      if (e.getActionCommand().startsWith("REMOVEDEADCODE")) {
         Main.startWork("Removing dead code...");
         final boolean all = e.getActionCommand().endsWith("ALL");
         if ((!all) || confirmExperimental()) {
            new SwingWorker() {
               @Override
               protected Object doInBackground() throws Exception {
                  int cnt = 0;
                  if (all) {
                     for (DoABCTag tag : abcPanel.list) {
                        cnt += tag.abc.removeDeadCode();
                     }
                  } else {
                     int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
                     if (bi != -1) {
                        cnt += abcPanel.abc.bodies[bi].removeDeadCode(abcPanel.abc.constants);
                     }
                     abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
                  }
                  Main.stopWork();
                  JOptionPane.showMessageDialog(null, "Instructions removed: " + cnt);
                  abcPanel.reload();
                  return true;
               }
            }.execute();
         }
      }

      if (e.getActionCommand().equals("RENAMEIDENTIFIERS")) {
         if (confirmExperimental()) {

            Main.startWork("Renaming identifiers...");
            new SwingWorker() {
               @Override
               protected Object doInBackground() throws Exception {
                  int cnt = 0;
                  HashMap<String, String> namesMap = new HashMap<String, String>();
                  for (DoABCTag tag : abcPanel.list) {
                     cnt += tag.abc.deobfuscateIdentifiers(namesMap);
                  }
                  Main.stopWork();
                  JOptionPane.showMessageDialog(null, "Identifiers renamed: " + cnt);
                  abcPanel.reload();
                  return true;
               }
            }.execute();


         }
      }

      if (e.getActionCommand().startsWith("DEOBFUSCATE")) {
         if (deobfuscationDialog == null) {
            deobfuscationDialog = new DeobfuscationDialog();
         }
         deobfuscationDialog.setVisible(true);
         if (deobfuscationDialog.ok) {
            Main.startWork("Deobfuscating...");
            new SwingWorker() {
               @Override
               protected Object doInBackground() throws Exception {
                  if (deobfuscationDialog.processAllCheckbox.isSelected()) {
                     for (DoABCTag tag : abcPanel.list) {
                        if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) {
                           tag.abc.removeDeadCode();
                        } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) {
                           tag.abc.removeTraps();
                        } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) {
                           tag.abc.removeTraps();
                           tag.abc.restoreControlFlow();
                        }
                     }
                  } else {
                     int bi = abcPanel.detailPanel.methodTraitPanel.methodCodePanel.getBodyIndex();
                     if (bi != -1) {
                        if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_DEAD_CODE) {
                           abcPanel.abc.bodies[bi].removeDeadCode(abcPanel.abc.constants);
                        } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_REMOVE_TRAPS) {
                           abcPanel.abc.bodies[bi].removeTraps(abcPanel.abc.constants);
                        } else if (deobfuscationDialog.codeProcessingLevel.getValue() == DeobfuscationDialog.LEVEL_RESTORE_CONTROL_FLOW) {
                           abcPanel.abc.bodies[bi].removeTraps(abcPanel.abc.constants);
                           abcPanel.abc.bodies[bi].restoreControlFlow(abcPanel.abc.constants);
                        }
                     }
                     abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setBodyIndex(bi, abcPanel.abc);
                  }
                  Main.stopWork();
                  JOptionPane.showMessageDialog(null, "Deobfuscation complete");
                  abcPanel.reload();
                  return true;
               }
            }.execute();
         }
      }
   }
   private int splitPos = 0;

   public void showDetail(String card) {
      CardLayout cl = (CardLayout) (detailPanel.getLayout());
      cl.show(detailPanel, card);
      if (card.equals(DETAILCARDEMPTYPANEL)) {
         if (detailPanel.isVisible()) {
            splitPos = splitPane2.getDividerLocation();
            detailPanel.setVisible(false);
         }
      } else {
         if (!detailPanel.isVisible()) {
            detailPanel.setVisible(true);
            splitPane2.setDividerLocation(splitPos);
         }
      }

   }

   public void showCard(String card) {
      CardLayout cl = (CardLayout) (displayPanel.getLayout());
      cl.show(displayPanel, card);
   }
   private Object oldValue;
   private File tempFile;

   @Override
   public void valueChanged(TreeSelectionEvent e) {
      Object tagObj = tagTree.getLastSelectedPathComponent();
      if (tagObj == null) {
         return;
      }

      if (tagObj instanceof TagNode) {
         tagObj = ((TagNode) tagObj).tag;
      }
      if (tagObj instanceof TreeElement) {
         tagObj = ((TreeElement) tagObj).getItem();
      }

      if (tagObj == oldValue) {
         return;
      }
      oldValue = tagObj;

      if (tagObj instanceof TreeLeafScript) {
         final TreeLeafScript scriptLeaf = (TreeLeafScript) tagObj;
         if (!Main.isWorking()) {
            Main.startWork("Decompiling...");
            (new Thread() {
               @Override
               public void run() {
                  int classIndex = -1;
                  for (Trait t : scriptLeaf.abc.script_info[scriptLeaf.scriptIndex].traits.traits) {
                     if (t instanceof TraitClass) {
                        classIndex = ((TraitClass) t).class_info;
                        break;
                     }
                  }
                  abcPanel.navigator.setABC(abcList, scriptLeaf.abc);
                  abcPanel.navigator.setClassIndex(classIndex);
                  abcPanel.setAbc(scriptLeaf.abc);
                  abcPanel.decompiledTextArea.setScript(scriptLeaf.abc.script_info[scriptLeaf.scriptIndex], scriptLeaf.abc, abcList);
                  abcPanel.decompiledTextArea.setClassIndex(classIndex);
                  abcPanel.decompiledTextArea.setNoTrait();
                  abcPanel.detailPanel.methodTraitPanel.methodCodePanel.setCode("");
                  Main.stopWork();
               }
            }).start();
         }
         showDetail(DETAILCARDAS3NAVIGATOR);
         showCard(CARDACTIONSCRIPTPANEL);
         return;
      } else {
         showDetail(DETAILCARDEMPTYPANEL);
      }
      if (tagObj instanceof DefineVideoStreamTag) {
         showCard(CARDEMPTYPANEL);
      } else if ((tagObj instanceof DefineSoundTag) || (tagObj instanceof SoundStreamHeadTag) || (tagObj instanceof SoundStreamHead2Tag)) {
         showCard(CARDEMPTYPANEL);
      } else if (tagObj instanceof ASMSource) {
         showCard(CARDACTIONSCRIPTPANEL);
         actionPanel.setSource((ASMSource) tagObj);
      } else if (tagObj instanceof DefineBitsTag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsTag) tagObj).getFullImageData(jtt));
      } else if (tagObj instanceof DefineBitsJPEG2Tag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsJPEG2Tag) tagObj).imageData);
      } else if (tagObj instanceof DefineBitsJPEG3Tag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsJPEG3Tag) tagObj).imageData);
      } else if (tagObj instanceof DefineBitsJPEG4Tag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsJPEG4Tag) tagObj).imageData);
      } else if (tagObj instanceof DefineBitsLosslessTag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsLosslessTag) tagObj).getImage());
      } else if (tagObj instanceof DefineBitsLossless2Tag) {
         showCard(CARDIMAGEPANEL);
         imagePanel.setImage(((DefineBitsLossless2Tag) tagObj).getImage());
      } else if ((tagObj instanceof FrameNode && ((FrameNode) tagObj).isDisplayed()) || (((tagObj instanceof CharacterTag) || (tagObj instanceof FontTag)) && (tagObj instanceof Tag))) {
         try {

            if (tempFile != null) {
               tempFile.delete();
            }
            tempFile = File.createTempFile("temp", ".swf");
            tempFile.deleteOnExit();

            FileOutputStream fos = new FileOutputStream(tempFile);
            SWFOutputStream sos = new SWFOutputStream(fos, 10);
            sos.write("FWS".getBytes());
            sos.write(13);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            SWFOutputStream sos2 = new SWFOutputStream(baos, 10);
            int width = swf.displayRect.Xmax - swf.displayRect.Xmin;
            int height = swf.displayRect.Ymax - swf.displayRect.Ymin;
            sos2.writeRECT(swf.displayRect);
            sos2.writeUI8(0);
            sos2.writeUI8(swf.frameRate);
            sos2.writeUI16(100); //framecnt
            sos2.writeTag(new SetBackgroundColorTag(new RGB(255, 255, 255)));

            if (tagObj instanceof FrameNode) {
               FrameNode fn = (FrameNode) tagObj;
               Object parent = fn.getParent();
               List<Object> subs = new ArrayList<Object>();
               if (parent == null) {
                  subs.addAll(swf.tags);
               } else {
                  if (parent instanceof Container) {
                     subs = ((Container) parent).getSubItems();
                  }
               }
               List<Integer> doneCharacters = new ArrayList<Integer>();
               int frameCnt = 1;
               for (Object o : subs) {
                  if (o instanceof ShowFrameTag) {
                     frameCnt++;
                     continue;
                  }
                  if (frameCnt > fn.getFrame()) {
                     break;
                  }
                  Tag t = (Tag) o;
                  Set<Integer> needed = t.getDeepNeededCharacters(characters);
                  for (int n : needed) {
                     if (!doneCharacters.contains(n)) {
                        sos2.writeTag(characters.get(n));
                        doneCharacters.add(n);
                     }
                  }
                  if (t instanceof CharacterTag) {
                     doneCharacters.add(((CharacterTag) t).getCharacterID());
                  }
                  sos2.writeTag(t);

                  if (parent != null) {
                     if (t instanceof PlaceObjectTypeTag) {
                        PlaceObjectTypeTag pot = (PlaceObjectTypeTag) t;
                        int chid = pot.getCharacterId();
                        int depth = pot.getDepth();
                        MATRIX mat = pot.getMatrix();
                        if (mat == null) {
                           mat = new MATRIX();
                        }
                        mat = (MATRIX) Helper.deepCopy(mat);
                        if (parent instanceof BoundedTag) {
                           RECT r = ((BoundedTag) parent).getRect(characters);
                           mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2;
                           mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2;
                        } else {
                           mat.translateX = mat.translateX + width / 2;
                           mat.translateY = mat.translateY + height / 2;
                        }
                        sos2.writeTag(new PlaceObject2Tag(false, false, false, false, false, true, false, true, depth, chid, mat, null, 0, null, 0, null));

                     }
                  }
               }
               sos2.writeTag(new ShowFrameTag());
            } else {

               if (tagObj instanceof DefineBitsTag) {
                  if (jtt != null) {
                     sos2.writeTag(jtt);
                  }
               } else if (tagObj instanceof AloneTag) {
               } else {
                  Set<Integer> needed = ((Tag) tagObj).getDeepNeededCharacters(characters);
                  for (int n : needed) {
                     sos2.writeTag(characters.get(n));
                  }
               }

               sos2.writeTag(((Tag) tagObj));

               int chtId = 0;
               if (tagObj instanceof CharacterTag) {
                  chtId = ((CharacterTag) tagObj).getCharacterID();
               }

               MATRIX mat = new MATRIX();
               mat.hasRotate = false;
               mat.hasScale = false;
               mat.translateX = 0;
               mat.translateY = 0;
               if (tagObj instanceof BoundedTag) {
                  RECT r = ((BoundedTag) tagObj).getRect(characters);
                  mat.translateX = -r.Xmin;
                  mat.translateY = -r.Ymin;
                  mat.translateX = mat.translateX + width / 2 - r.getWidth() / 2;
                  mat.translateY = mat.translateY + height / 2 - r.getHeight() / 2;
               } else {
                  mat.translateX = width / 4;
                  mat.translateY = height / 4;
               }
               if (tagObj instanceof FontTag) {

                  int countGlyphs = ((FontTag) tagObj).getGlyphShapeTable().length;
                  int fontId = ((FontTag) tagObj).getFontId();
                  int sloupcu = (int) Math.ceil(Math.sqrt(countGlyphs));
                  int radku = (int) Math.ceil(((float) countGlyphs) / ((float) sloupcu));
                  int x = 0;
                  int y = 1;
                  for (int f = 0; f < countGlyphs; f++) {
                     if (x >= sloupcu) {
                        x = 0;
                        y++;
                     }
                     List<TEXTRECORD> rec = new ArrayList<TEXTRECORD>();
                     TEXTRECORD tr = new TEXTRECORD();
                     int textHeight = height / radku;
                     tr.fontId = fontId;
                     tr.styleFlagsHasFont = true;
                     tr.textHeight = textHeight;
                     tr.glyphEntries = new GLYPHENTRY[1];
                     tr.styleFlagsHasColor = true;
                     tr.textColor = new RGB(0, 0, 0);
                     tr.glyphEntries[0] = new GLYPHENTRY();
                     tr.glyphEntries[0].glyphAdvance = 0;
                     tr.glyphEntries[0].glyphIndex = f;
                     rec.add(tr);
                     mat.translateX = x * width / sloupcu;
                     mat.translateY = y * height / radku;
                     sos2.writeTag(new DefineTextTag(999 + f, new RECT(0, width, 0, height), new MATRIX(), SWFOutputStream.getNeededBitsU(countGlyphs - 1), SWFOutputStream.getNeededBitsU(0), rec));
                     sos2.writeTag(new PlaceObject2Tag(false, false, false, true, false, true, true, false, 1 + f, 999 + f, mat, null, 0, null, 0, null));
                     x++;
                  }
                  sos2.writeTag(new ShowFrameTag());
               } else if ((tagObj instanceof DefineMorphShapeTag) || (tagObj instanceof DefineMorphShape2Tag)) {
                  sos2.writeTag(new PlaceObject2Tag(false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null));
                  sos2.writeTag(new ShowFrameTag());
                  int numFrames = 100;
                  for (int ratio = 0; ratio < 65536; ratio += 65536 / numFrames) {
                     sos2.writeTag(new PlaceObject2Tag(false, false, false, true, false, true, false, true, 1, chtId, mat, null, ratio, null, 0, null));
                     sos2.writeTag(new ShowFrameTag());
                  }
               } else {
                  sos2.writeTag(new PlaceObject2Tag(false, false, false, true, false, true, true, false, 1, chtId, mat, null, 0, null, 0, null));
                  sos2.writeTag(new ShowFrameTag());
               }
            }//not showframe

            sos2.writeTag(new EndTag());
            byte data[] = baos.toByteArray();

            sos.writeUI32(sos.getPos() + data.length + 4);
            sos.write(data);
            fos.close();
            showCard(CARDFLASHPANEL);
            if (flashPanel != null) {
               if (flashPanel instanceof FlashPlayerPanel) {
                  flashPanel.displaySWF(tempFile.getAbsolutePath());
               }
            }

         } catch (Exception ex) {
            Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex);
         }

      } else {
         showCard(CARDEMPTYPANEL);
      }
   }
}
